/**
 * Copyright (c) 2020 kedacom
 * OpenATC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 **/
package com.openatc.agent.controller;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.openatc.agent.model.*;
import com.openatc.agent.service.*;
import com.openatc.agent.service.impl.CommonService;
import com.openatc.comm.common.CosntDataDefine;
import com.openatc.agent.service.impl.WebOperationCollector;
import com.openatc.agent.service.impl.WebOperationRecordExtractor;
import com.openatc.comm.data.MessageData;
import com.openatc.core.common.IErrorEnumImplOuter;
import com.openatc.core.model.RESTRet;
import com.openatc.core.model.RESTRetBase;
import com.openatc.core.util.MyHttpUtil;
import com.openatc.core.util.RESTRetUtils;
import com.openatc.extend.common.aop.model.SysLog;
import com.openatc.model.model.*;
import com.openatc.model.service.ManualpanelService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import static com.openatc.core.common.Constants.PERMISSION_CANALIZATION;
import static com.openatc.core.common.IErrorEnumImplOuter.*;

@RestController
@CrossOrigin
public class DevController {
    private static Logger logger = Logger.getLogger(DevController.class.toString());

    @Autowired(required = false)
    AscsDao mDao;

    @Autowired(required = false)
    private UserDao userDao;
    @Autowired(required = false)
    private OrgService orgService;
    @Autowired
    VipRouteDeviceDao vipRouteDeviceDao;
    @Autowired
    RouteIntersectionDao routeIntersectionDao;
//    @Autowired
//    private MessageController messageController;
    @Autowired
    protected RestTemplate restTemplate;
    @Autowired
    private DevService devService;
    @Autowired
    private MessageService messageService;

    @Autowired
    private CommonService commonService;

    private Gson gson = new Gson();
    private Logger log = Logger.getLogger(DevController.class.toString());

    /**
     * 根据用户名查询设备
     * @param username
     * @return
     */
    @GetMapping(value = "/devs/user/{username}")
    public RESTRetBase GetAllAscsByUsername(@PathVariable String username) {
//        User user = userDao.getUserByUserName(username);
//        String organizationName = user.getOrganization();
//        if (organizationName == null) return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_3016);
//        List<SysOrg> sysOrgs = orgService.findByOrgnizationCodeLike(organizationName);
//        if (sysOrgs == null) return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_3017);
//        List<AscsBaseModel> devices = new ArrayList<>();
//        for (SysOrg sysOrg : sysOrgs) {
//            List<AscsBaseModel> ascsByCode = mDao.getAscsLikeCode(sysOrg.getOrgnization_code());
//            devices.addAll(ascsByCode);
//        }
//        return RESTRetUtils.successObj(devices);
        List<AscsBaseModel> devs = devService.getDevsByUserName(username);
        return RESTRetUtils.successObj(devs);
    }

    // 按组织机构查询设备
    @GetMapping(value = "/devs/orgnization/{code}")
    public RESTRetBase getAscsByCode(@PathVariable String code) {
        if(code == null || code.isEmpty() || code.equals("*"))
            return RESTRetUtils.successObj(mDao.getAscsLikeCode("*"));
        else
            return RESTRetUtils.successObj(mDao.getAscsLikeCode(code));
    }

    // 查询所有没有组织机构的设备
    @GetMapping(value = "/devs/orgnization/none")
    public RESTRetBase getAscsNoCode() {
        return RESTRetUtils.successObj(mDao.getAscsNoCode());
    }

    /**
     * 查询当前用户名下的所有设备
     * @return
     */
    @GetMapping(value = { "/devs" ,  "/devs/all"})
    public RESTRet GetDevs() {
//        List<AscsBaseModel> ascsBaseModels = mDao.getAscs();
//        return devService.getDevs();

        List<AscsBaseModel> ascsBaseModels = devService.getDevs();
        return RESTRetUtils.successObj(ascsBaseModels);
    }

    // 按ID查询设备
    @GetMapping(value = { "/devs/{id}" , "/devs/info/{id}" })
    public RESTRetBase GetAscsInfo(@PathVariable String id) {
        return RESTRetUtils.successObj(mDao.getAscsByID(id));
    }

    // 按类型查询设备
    @GetMapping(value = "/devs/type/{type}")
    public RESTRetBase GetDevByType(@PathVariable String type) {
        return RESTRetUtils.successObj(mDao.getAscsByType(type));
    }

    @PostMapping(value = "/devs/range")
    public RESTRetBase getDevsRange(@RequestBody JsonObject jsonObject) {
//        return devService.getRangeDevs(jsonObject);
        return RESTRetUtils.successObj(mDao.getAscsRange(jsonObject));
    }
    // 按列表中的路口ID获取路口
    @PostMapping(value = "/devs/list")
    public RESTRetBase getDevList(@RequestBody JsonObject jsonObject) {
        Gson gson = new Gson();
        List<AscsBaseModel> devList = new ArrayList<>();
        List<String> routeIntersectionIdList = gson.fromJson(jsonObject.get("routeIntersectionIdList"), List.class);
        if (!CollectionUtils.isEmpty(routeIntersectionIdList)) {
            devList = mDao.getDevListByIds(routeIntersectionIdList);
        }
//        for (String routeIntersectionId : routeIntersectionIdList) {
//            AscsBaseModel device = null;
//            try {
//                device = mDao.getAscsByID(routeIntersectionId);
//                if(device == null)
//                    return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_8001);
//
//            } catch (EmptyResultDataAccessException e) {
//                return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_8001);
//            }
//            devList.add(device);
//        }
//        mDao.alterStatus(devList);
        return RESTRetUtils.successObj(devList);
    }

    // 按ID删除设备
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
    @RequiresPermissions("openatc:base:crossedit")
    @DeleteMapping(value = "/devs/{id}")
    public RESTRetBase DeleteDev(@PathVariable String id) {
        AscsBaseModel as = mDao.getAscsByID(id);
        //check oldAgentid if in use
        String oldAgentid = as.getAgentid();
        IErrorEnumImplOuter iErrorEnumImplOuter = checkDevInUse(oldAgentid);
        if(iErrorEnumImplOuter !=null) {
            return RESTRetUtils.errorObj(false,iErrorEnumImplOuter);
        }
        mDao.deleteDevByID(id);
        return RESTRetUtils.successObj(as);
    }

    /**
     * @Author: yangyi
     * @Date: 2022/2/16 14:48
     * @Description: checkDevInUse
     */
    public IErrorEnumImplOuter checkDevInUse (String oldAgentid) {
        // 瓶颈控制放到商业版中
        //        List<Overflow> overflowList = overflowRepository.findByIntersectionid(oldAgentid);
//        if(overflowList !=null && overflowList.size() > 0) {
//            return IErrorEnumImplOuter.E_8008;
//        }
        List<VipRouteDevice> vipRouteDeviceList =  vipRouteDeviceDao.findByAgentid(oldAgentid);
        if(vipRouteDeviceList !=null && vipRouteDeviceList.size() > 0) {
            return IErrorEnumImplOuter.E_8003;
        }
        List<RouteIntersection> routeIntersectionList =  routeIntersectionDao.findByAgentid(oldAgentid);
        if(routeIntersectionList !=null && routeIntersectionList.size() > 0) {
            return IErrorEnumImplOuter.E_8002;
        }
        return null;
    }

    // 添加设备
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
    @RequiresPermissions("openatc:base:crossedit")
    @PostMapping(value = "/devs")
    public RESTRetBase InsertDev(@RequestBody AscsBaseModel ascs) {
//        String name = ascs.getName();
//        if (name == null || name.equals("")) {
//            ascs.setName(ascs.getAgentid());
//        }
        // 路段id和路口id不能重复
        int sectionCount = devService.getSectionCount(ascs.getAgentid());
        if (sectionCount != 0) {
            return RESTRetUtils.errorObj(E_8011);
        }
        int count = mDao.getCountByAgentid(ascs.getAgentid());
        if (count != 0) {
            return RESTRetUtils.errorObj(E_8004);
        }
        return RESTRetUtils.successObj(mDao.insertDev(ascs));
    }

    // 更新设备
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
    @RequiresPermissions("openatc:base:crossedit")
    @PutMapping(value = "/devs")
    public RESTRetBase UpdateDev(@RequestBody AscsBaseModel ascs) {
        int temp = mDao.updateDev(ascs);
        if (temp == 0) {
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_2002);
        } else {
            return RESTRetUtils.successObj(ascs);
        }
    }

    // 信号机的注册消息处理
//    @PutMapping(value = "/devs/discovery")
//    public RESTRetBase DevAscsDiscovery(@RequestBody DevCover ascsModel){
//        mDao.updateAscsByReport(ascsModel);
////        mDao.updateAscs(ascsModel);
//        return RESTRetUtils.successObj(ascsModel);
//    }

    // 修改设备ID
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
    @RequiresPermissions("openatc:base:crossedit")
    @PostMapping(value = "/devs/agentid")
    public RESTRetBase modifyAgentid(@RequestBody JsonObject jsonObject) {
        String oldAgentid = jsonObject.get("oldAgentid").getAsString();
        String newAgentid = jsonObject.get("newAgentid").getAsString();
        // 检查要修改的设备id是否和路段id重复
        int sectionCount = devService.getSectionCount(newAgentid);
        if (sectionCount != 0) {
            return RESTRetUtils.errorObj(E_8011);
        }
        //check oldAgentid if in use
        IErrorEnumImplOuter iErrorEnumImplOuter = checkDevInUse(oldAgentid);
        if(iErrorEnumImplOuter !=null) {
            return RESTRetUtils.errorObj(false,iErrorEnumImplOuter);
        }
        AscsBaseModel dev = mDao.getAscsByID(newAgentid);
        if (dev != null) {
            return RESTRetUtils.errorObj(false,IErrorEnumImplOuter.E_8004);
        }
        boolean result = mDao.modifyAgentid(oldAgentid, newAgentid);
        if(!result)
            return RESTRetUtils.errorObj(E_0006);

        return RESTRetUtils.successObj(true);
    }

    // 锁定交通流
    @PostMapping(value = "/devs/{agentid}/lockdirection")
    public RESTRetBase DevAscsDiscovery(@PathVariable String agentid, @RequestBody LockDirection lockDirection){

        // 获取路口相位和通道参数
        MessageData messageData = new MessageData(agentid, CosntDataDefine.getrequest, CosntDataDefine.allfeature);
//        RESTRet<MessageData> restRet = messageController.postDevsMessage(null, messageData);
        RESTRet<MessageData> restRet = messageService.postDevsMessage(messageData);
        if(restRet.isSuccess()){

            JsonObject feature = restRet.getData().getData().getAsJsonObject();
            JsonArray phaseArray = feature.get("phaseList").getAsJsonArray();
            JsonArray channelArray = feature.get("channelList").getAsJsonArray();

            // 将锁定交通流参数转换为设备方向锁定消息
            ManualpanelService manualpanelService = new ManualpanelService();
            ControlPattern data = manualpanelService.LockDirection2ControlPattern(phaseArray,channelArray,lockDirection);
            messageData = new MessageData(agentid, CosntDataDefine.setrequest, CosntDataDefine.ControlPattern, gson.toJsonTree(data));
            String str = gson.toJson(messageData);
            log.info("Lock Direction AgentID:" + agentid + " " + str);
//            restRet = messageController.postDevsMessage(null, messageData);
            restRet = messageService.postDevsMessage(messageData);
        }

        return restRet;
    }

    /**
     * 获取昨日0至24点设备统计状态，只返回管理员的数据
     * @author: zhangwenchao
     * @return
     */
    @GetMapping(value = { "/devs/statuscollect/yesterday"})
    public RESTRetBase GetDevsStatuscollectYesterday() {
        // 管理员列表
        List<String> adminRoles = new ArrayList<>();
        adminRoles.add("superadmin");
        adminRoles.add("admin");

        // 获取当前登录用户信息
        User user = (User) SecurityUtils.getSubject().getPrincipal();

        // 未开启shiro
        if (user == null){
            // 返回所有设备
            return RESTRetUtils.successObj(mDao.statesCollectYesterday);
        }

        // 获取用户名
        String user_name = user.getUser_name();
        List<String> roles = userDao.getRoleNamesByUsername(user_name);
        adminRoles.retainAll(roles);
        // 非管理员
        if (adminRoles.size() == 0){
//            return RESTRetUtils.errorObj(E_8009);
            return RESTRetUtils.successObj();
        }

        return RESTRetUtils.successObj(mDao.statesCollectYesterday);

    }

    /**
     * 保存路口渠化图
     * @param jsonObject
     * @return
     */
    @RequiresPermissions(PERMISSION_CANALIZATION)
    @PostMapping(value = "/devs/channelizatonchart")
    public RESTRet createIntChannelizationChart(@RequestBody JsonObject jsonObject){
        String agentid = jsonObject.get("agentid").getAsString();
        JsonElement data = jsonObject.get("data");

        if (!StringUtils.hasLength(agentid) || data == null){
            return RESTRetUtils.errorObj(false,IErrorEnumImplOuter.E_1001);
        }

        int row = mDao.saveIntChannelizatonChart(agentid,data);

        if (row == 0){
            return RESTRetUtils.errorObj(false, E_0006);
        }
        return RESTRetUtils.successObj("success");
    }

    /**
     * 获取路口渠化图
     * @param agentid
     * @return
     */
    @GetMapping(value = "/devs/channelizatonchart/{agentid}")
    public RESTRet getIntChannelizationChart(@PathVariable String agentid){
        String data = mDao.getIntChannelizationChartByAgentid(agentid);
        if (data == null || "{}".equals(data)){
            return RESTRetUtils.errorObj(false,IErrorEnumImplOuter.E_2002);
        }
        try {
            return RESTRetUtils.successObj(gson.fromJson(data, JsonObject.class));
        } catch (JsonSyntaxException e){
            return RESTRetUtils.errorObj(false,IErrorEnumImplOuter.E_2007);
        }
    }

    /**
     * 向信号机批量下发特征参数或控制消息
     * @param messages
     * @return
     */
    @PostMapping(value = "/devs/messages")
    public RESTRetBase postDevMessages(@RequestBody JsonObject messages){
//        // 遇到下发消息失败的信号机是否继续下发其他设备的消息
        boolean continueOnfailure = messages.get("continueOnfailure").getAsBoolean();
        List<MessageData> messageDataList = gson.fromJson(messages.get("messages"),new TypeToken<List<MessageData>>(){}.getType());

        JsonObject statusdata = new JsonObject();

        JsonArray data = new JsonArray();
        statusdata.add("statusdata",data);

        for (MessageData requestData : messageDataList) {
            // 向信号机发送消息
//            RESTRet ret = messageController.postDevsMessage(null, requestData);
            RESTRet ret = messageService.postDevsMessage(requestData);
            boolean successSendMsg = commonService.isSuccessSendMsg(ret);
            JsonElement currentAgentStatus = commonService.addPropertyForRet(ret, requestData.getAgentid());
            data.add(currentAgentStatus);
            // 如果遇到失败，且指定下发失败就不再继续下发，直接应答当前状态
            if (!continueOnfailure && !successSendMsg) {
                return RESTRetUtils.successObj(statusdata);
            }
        }
        return RESTRetUtils.successObj(statusdata);
    }

    /**
     * 查询当前路口检测器状态
     * @param agentid
     * @return
     */
    @GetMapping(value = "/devs/detectorstatus/{agentid}")
    public RESTRetBase getDetectorStatusByAgentid(@PathVariable String agentid) {
        StatusDetector detectorStatus = devService.getDetectorStatusByAgentid(agentid);
        return RESTRetUtils.successObj(detectorStatus);
    }

    /**
     * 根据路口id从渠化图中获取路口车道
     * @param agentid
     * @return
     */
    @GetMapping("/devs/laneparam/{agentid}")
    public RESTRetBase getIntersectionLaneParam(@PathVariable String agentid) {
        String data = mDao.getIntChannelizationChartByAgentid(agentid);

        if (data == null || "{}".equals(data)) {
            return RESTRetUtils.errorObj(false,IErrorEnumImplOuter.E_2002);
        }

        List<Vehile> vehiles =  devService.getIntersectionLaneParam(data);
        Interfeature interfeature = new Interfeature();
        interfeature.setVehiledataList(vehiles);
        return RESTRetUtils.successObj(interfeature);
    }

    /**
     * 增加路口事件
     * @param jsonObject 路口事件
     * @return
     */
    @PostMapping(value = "/devs/event/add")
    public RESTRet addEvent(@RequestBody JsonObject jsonObject){
        String agentid = jsonObject.get("agentid").getAsString();
        int eventType  = jsonObject.get("type").getAsInt();
        String eventTime = jsonObject.get("time").getAsString();

        int row = mDao.addEvent(agentid,eventType,eventTime);

        if (row == 0){
            return RESTRetUtils.errorObj(false, E_0006);
        }
        return RESTRetUtils.successObj("success");
    }

    /**
     * 重置路口事件
     * @param jsonObject 路口事件
     * @return
     */
    @PostMapping(value = "/devs/event/reset")
    public RESTRet resetEvent(@RequestBody JsonObject jsonObject){
        String agentid = jsonObject.get("agentid").getAsString();

        int row = mDao.resetEvent(agentid);

        if (row == 0){
            return RESTRetUtils.errorObj(false, E_0006);
        }
        return RESTRetUtils.successObj("success");
    }

    /**
     * 删除路口事件
     * @param jsonObject 路口事件
     * @return
     */
    @PostMapping(value = "/devs/event/remove")
    public RESTRet removeEvent(@RequestBody JsonObject jsonObject){
        String agentid = jsonObject.get("agentid").getAsString();
        int eventType  = jsonObject.get("type").getAsInt();

        int row = mDao.removeEvent(agentid,eventType);

        if (row == 0){
            return RESTRetUtils.errorObj(false, E_0006);
        }
        return RESTRetUtils.successObj("success");
    }


    /**
     * 获取路口中车道方向
     * @param agentid
     * @return
     */
    @GetMapping(value = "/devs/direction/{agentid}")
    public RESTRetBase getLaneDirection(@PathVariable String agentid) {

        JsonArray directions = devService.getLaneDirection(agentid);

        JsonObject resData = new JsonObject();
        resData.addProperty("agentid",agentid);
        resData.add("data",directions);

        return RESTRetUtils.successObj(resData);
    }

    /**
     * 更新信号机的设备密钥
     * 平台收到注册消息后，先查询设备ID：
     * 如果有多个相同的设备ID，应答错误
     * 如果没有设备ID，应答错误
     * 如果已经存在设备ID，则调用该配置软件的接口，更新信号机的密钥文件
     * @param devCover
     * @return
     */
    @PostMapping(value = "/devs/auth/update")
    public RESTRet updateDevAuth(@RequestBody DevCover devCover) {
        String siteID = String.valueOf(devCover.getId());
        List<AscsBaseModel> devs = mDao.getAscsBySiteID(siteID);
        if(devs.size() > 1) // 大于1个报错
            return RESTRetUtils.errorObj(false,E_8012);
        else if (devs.size() == 0) { // 如果没有设备ID，应答错误
            return RESTRetUtils.errorObj(false,E_8013);
        }
        else{ // 查找到唯一设备，发送给设备的配置软件来更新设备密钥
            AscsBaseModel ascsBaseModel = devs.get(0);
            String ip = ascsBaseModel.getJsonparam().get("ip").getAsString();
            String url = "http://" + ip + ":8012/openatc/devs/auth";
            String json = MyHttpUtil.doPost(url, new JsonObject().toString());
            if( json.isEmpty() )
                return RESTRetUtils.errorObj(false,IErrorEnumImplOuter.E_8015);;

            RESTRet ret = gson.fromJson(json, RESTRet.class);
            if(ret.isSuccess())
                ret = loginDevAuth(devCover);
            return ret;
        }
    }

    /**
     * 使用配置软件在平台上注册设备并更新密钥，配置软件先下载设备信息更新设备ID和平台信息，然后上报注册消息。
     * 平台收到注册消息后，先查询设备ID：
     * 如果有多个相同的设备ID，应答错误
     * 如果没有设备ID，则新增一个设备
     * 如果已经存在设备ID，则更新设备
     * @param devCover
     * @return
     */
    // 此接口仅用于实体信号机的密钥注册功能，其他信号机不需要调用此接口
    @PostMapping(value = "/devs/auth/login")
    public RESTRet loginDevAuth(@RequestBody DevCover devCover) {
        String siteID = String.valueOf(devCover.getId());
        String secretkey = devCover.getSecretkey();
        if(secretkey.isEmpty())
            return RESTRetUtils.errorObj(false,E_8014);


        List<AscsBaseModel> devs = mDao.getAscsBySiteID(siteID);
        if(devs.size() > 1) // 大于1个报错
            return RESTRetUtils.errorObj(false,E_8012);
        else if (devs.size() == 1) { // 只有一个，更新设备密钥
            mDao.updateSecret(devs.get(0).getAgentid(), secretkey );
        } else if (devs.size() == 0) { // 没有则新增,并刷新设备表
            String ip = devCover.getIp();
            // 新增设备必须包含IP信息
            if( ip == null || ip.isEmpty())
                return RESTRetUtils.errorObj(false,E_8016);

            devCover.setPort(8880);
            devCover.setType("asc");
            devCover.setProtocol("ocp");
            mDao.insertAscsByReportLogin(devCover,siteID,siteID);
            mDao.updateSecret(siteID, secretkey );

        }

        return RESTRetUtils.successObj("success");
    }

    /**
     * 根据设备ID查询路口
     * @param siteid
     * @return
     */
    @GetMapping(value = "/devs/siteid/{siteid}")
    public RESTRet getDevBySiteid(@PathVariable String siteid) {
        List<AscsBaseModel> devs = mDao.getAscsBySiteID(siteid);
        return RESTRetUtils.successObj(devs);
    }
}